package edu.bu.manateem.msg.analysis.util;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import edu.bu.manateem.msg.MSGConstants;
import edu.bu.manateem.msg.model.MassScan;


import org.systemsbiology.jrap.MSXMLParser;
import org.systemsbiology.jrap.Scan;

/**
 * A collection of static methods related to mzXML files
 * @author gindiny
 *
 */

public class MzXmlUtils
{
	private static final String SCAN_NUM = "<scan num=";
	private static final String RETENTION_TIME = "retentionTime";
	private static final Pattern RETENTION_TIME_PATTERN =
		Pattern.compile("[.0123456789]");
	
	/**
	 * Given a string like retentionTime="PT2.668S"
	 * returns PT2.668S
	 * @param input
	 * @return
	 */
	public static String cleanScanTimeStamp(String input)
	{
		String[] subValue = input.split(MSGConstants.EQUALS);
		// String[] subValue = StringUtils.split(input,ClientConstants.EQUALS);
		return subValue[1].replaceAll(MSGConstants.DOUBLE_QUOTES,"");
		// return StringUtils.remove(subValue[1],ClientConstants.DOUBLE_QUOTES);
	}
	
	/**
	 * 
	 * @param retentionTimesToScans
	 * @param minTime inclusive
	 * @param maxTime inclusive
	 * @return
	 */
	public static List<Integer> getScansFromRetentionTimes(
		Map<Double,Integer> retentionTimesToScans,double minTime,double maxTime)
	{
		List<Integer> scanNumbers = new ArrayList<Integer>();
		
		for (double key : retentionTimesToScans.keySet())
		{
			if ((key >= minTime) && (key <= maxTime))
			{
				scanNumbers.add(retentionTimesToScans.get(key));
			}
		}
		return scanNumbers;
	}
	
	/**
	 * Sums up intensity values given a range of m/z values
	 * @param scan
	 * @param minMz inclusive
	 * @param maxMz inclusive
	 * @return sum
	 */
	public static double getScanSliceIntesitySum(MassScan scan,double minMz,
		double maxMz)
	{
		float[][] scanValues = scan.getScanValues();
		double result = 0.0;
		
		for (int i = 0; i < scanValues[0].length; i++)
		{
			double mass = scanValues[0][i];
			if ((mass >= minMz) && (mass <= maxMz))
			{
				result += scanValues[1][i];
			}
		}
		
		/*
		 * SortedMap<Float, Float> massToIntensity = new TreeMap<Float,
		 * Float>();
		 * for (int i = 0; i < scanValues[0].length; i++)
		 * {
		 * massToIntensity.put(scanValues[0][i], scanValues[1][i]);
		 * }
		 * for (Float mass : massToIntensity.subMap(minMz, maxMz +
		 * Float.MIN_VALUE).keySet()) // inclusive inclusive
		 * {
		 * result += massToIntensity.get(mass);
		 * }
		 */
		return result;
	}
	
	/**
	 * Map retention time to scan.
	 * Time units are not assumed.
	 * @deprecated - use the getTimeToScanMap(File mzXmlFile)
	 * @param parser
	 * @return
	 */
	@Deprecated
	public static Map<Double,Integer> getTimeToScanMap(MSXMLParser parser)
	{
		Map<Double,Integer> map = new HashMap<Double,Integer>();
		Scan scan;
		Double retentionTime;
		
		for (int i = 1,n = parser.getScanCount(); i <= n; i++)
		{
			scan = parser.rap(i);
			// retentionTime = new Double
			// (StringUtils.strip(scan.getRetentionTime(), ALPHA));
			retentionTime =
				MzXmlUtils.parseRetentionTime(scan.getRetentionTime());
			map.put(retentionTime,i);
		}
		return map;
	}
	
	/**
	 * Map retention time to scan.
	 * Time units are not assumed
	 * @param mzXmlFile
	 * @return
	 */
	public static Map<Double,Integer> getTimeToScanMap(String mzXmlFile,
		MSXMLParser parser)
	{
		Map<Double,Integer> map = new HashMap<Double,Integer>();
		
		try
		{
			RandomAccessFile file = new RandomAccessFile(mzXmlFile,"r");
			for (int i = 1,n = parser.getScanCount(); i <= n; i++)
			{
				long offset = parser.getScanOffset(i);
				file.seek(offset);
				String line;
				Integer scanNumber = null;
				while ((line = file.readLine()) != null)
				{
					// line = StringUtils.strip(line);
					line = line.substring(0,line.length() - 1);
					if (line.contains(MzXmlUtils.SCAN_NUM))
					// if (StringUtils.contains(line,MzXmlUtils.SCAN_NUM))
					{
						scanNumber =
							Integer.parseInt(MzXmlUtils.cleanScanTimeStamp(line));
					}
					// Assuming here that retention time will always follow scan
					// number
					else if (line.contains(MzXmlUtils.RETENTION_TIME))
					// else if (StringUtils.contains(line,
					// MzXmlUtils.RETENTION_TIME))
					{
						// String retTime = parseOutValue(line);
						map.put(MzXmlUtils.parseRetentionTime(line),scanNumber);
						
						// will jump to the next scan
						break;
					}
				}
			}
		}
		catch (FileNotFoundException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return map;
	}
	
	public static double parseRetentionTime(String time)
	{
		
		StringBuffer resultHoder = new StringBuffer();
		Matcher matcher = MzXmlUtils.RETENTION_TIME_PATTERN.matcher(time);
		
		while (matcher.find())
		{
			resultHoder.append(matcher.group());
		}
		
		return Double.parseDouble(resultHoder.toString());
	}
}
